package com.xiaoq.positionmarket.endpoint;

import com.alibaba.dubbo.config.annotation.Reference;
import com.xiaoq.BaseEndpoint2B;
import com.xiaoq.apis.sites.ISitesApi;
import com.xiaoq.apis.sites.SitesInfo4Activity;
import com.xiaoq.apis.user.IUserApi;
import com.xiaoq.apis.user.UserPhone4PositionVo;
import com.xiaoq.coms.GenericDataResponse;
import com.xiaoq.coms.GenericListResponse;
import com.xiaoq.coms.GenericResponse;
import com.xiaoq.coms.util.ConstantUtil;
import com.xiaoq.coms.util.IdGenerator;
import com.xiaoq.jobs.PositionMarketJob;
import com.xiaoq.positionmarket.entity.CityInfo;
import com.xiaoq.positionmarket.entity.PositionMarketEntity;
import com.xiaoq.positionmarket.entity.PositionPushRecordEntity;
import com.xiaoq.positionmarket.entity.UserTrackEntity;
import com.xiaoq.positionmarket.service.IPositionMarketService;
import com.xiaoq.positionmarket.service.IPositionPushRecordService;
import com.xiaoq.positionmarket.service.IUserTrackService;
import com.xiaoq.positionmarket.vo.PositionMarket4InsertVo;
import com.xiaoq.positionmarket.vo.PositionMarketVo;
import com.xiaoq.vo.PagingVo;
import io.swagger.annotations.*;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.quartz.JobKey;
import org.quartz.Trigger;
import org.quartz.TriggerKey;
import org.quartz.impl.JobDetailImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.web.bind.annotation.*;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.List;

import static org.quartz.TriggerBuilder.newTrigger;

/**
 * Created by chenjh on 2018/4/2.
 */
@RestController
@RequestMapping("/api/v1/positionmarket")
@Api(tags = "位置营销API")
public class PositionMarketEndPoint2B extends BaseEndpoint2B{

    private static class PositionMarketListResponse extends GenericListResponse<PositionMarketVo>{}
    private static class SendNumDataResponse extends GenericDataResponse<SendNumVo> {}



    @Autowired
    private IPositionMarketService positionMarketService;

    @Autowired
    private IPositionPushRecordService positionPushRecordService;

    @Autowired
    private IUserTrackService userTrackService;

    @Autowired
    private IdGenerator idGenerator;

    @Reference(interfaceClass=IUserApi.class, check=false, version="1.0.0")
    private IUserApi userApi;

    @Reference(interfaceClass=ISitesApi.class, check=false, version="1.0.0")
    private ISitesApi sitesApi;

    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;


    @ApiOperation(value = "发送列表", notes = "发送列表")
    @ApiResponses({ @ApiResponse(code = 200, response = PositionMarketListResponse.class, message = "发送列表") })
    @GetMapping(value = "", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE })
    public ResponseEntity<?> list(@ApiParam(value="pn", required=false) @RequestParam(required = false, defaultValue = "1") int pn,
                                  @ApiParam(value="ps", required=false) @RequestParam(required = false, defaultValue = "10") int ps){
        PagingVo pagingVo = new PagingVo(pn, ps);
        String shopId = getShopId();
        List<PositionMarketVo> positionMarketVoList = new ArrayList<>();
        List<PositionMarketEntity> posiMarketList = positionMarketService.getPositionMarketListByPage(shopId, pagingVo);
        long totalCount = positionMarketService.getPositionMarketCountByPage(shopId);
        if(CollectionUtils.isNotEmpty(posiMarketList)){
            for (PositionMarketEntity positionMarketEntity : posiMarketList){
                PositionMarketVo positionMarketVo = new PositionMarketVo();
                BeanUtils.copyProperties(positionMarketEntity, positionMarketVo);
                positionMarketVoList.add(positionMarketVo);
            }
        }

        return PositionMarketListResponse.listAndCount(positionMarketVoList, totalCount);
    }


    @ApiOperation(value = "删除位置营销", notes = "删除位置营销")
    @ApiResponses({ @ApiResponse(code = 200, response = GenericResponse.class, message = "删除位置营销") })
    @DeleteMapping(value = "/{positionMarketId}", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE })
    public ResponseEntity<?> delete(@ApiParam(value="positionMarketId 主键", required=true) @PathVariable(required = true) String positionMarketId){

        boolean retFlag = positionMarketService.deleteById(positionMarketId);
        if(retFlag){

            String jobKeyName = ConstantUtil.JOB_KEY_PREFIX_4_POSITION_MARKET + positionMarketId;
            JobKey jobKey = new JobKey(jobKeyName, ConstantUtil.JOB_GROUP_NAME_4_POSITION_MARKET);
            try {
                if (schedulerFactoryBean.getScheduler().checkExists(jobKey)) {
                    schedulerFactoryBean.getScheduler().deleteJob(jobKey);
                    return GenericResponse.ok();
                }else {
                    return GenericResponse.ok();
                }
            }catch (Exception e){
                logger.error("删除定时任务失败" + e.getMessage());
            }
        }

        return GenericResponse.ng("删除失败");
    }


/*    @ApiOperation(value = "取消位置营销定时任务", notes = "取消位置营销定时任务")
    @ApiResponses({ @ApiResponse(code = 200, response = GenericResponse.class, message = "取消位置营销定时任务") })
    @DeleteMapping(value = "/schedule/{positionMarketId}", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE })
    public ResponseEntity<?> cancelSchedule(@ApiParam(value="positionMarketId 主键", required=true) @PathVariable(required = true) String positionMarketId){

        String jobKeyName = "jobKey:positionMarketId:" + positionMarketId;
        JobKey jobKey = new JobKey(jobKeyName, "job_4_postition_push");
        try {
            if (schedulerFactoryBean.getScheduler().checkExists(jobKey)) {
                schedulerFactoryBean.getScheduler().deleteJob(jobKey);
                return GenericResponse.ok();
            }else {
                return GenericResponse.ok();
            }
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return GenericResponse.ng("取消位置营销定时任务失败");
    }*/


    @ApiOperation(value = "计算推送覆盖的人数", notes = "计算推送覆盖的人数")
    @ApiResponses({ @ApiResponse(code = 200, response = SendNumDataResponse.class, message = "计算推送覆盖的人数") })
    @PostMapping(value = "/calculate/count", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE })
    public ResponseEntity<?> calculateCount(@RequestBody PositionMarket4InsertVo positionMarketVo){

        String shopId = getShopId();

        int pushType = positionMarketVo.getType()==null?0:positionMarketVo.getType();
        long sendNum = 0L;
        if(pushType == ConstantUtil.PUSH_BY_CITY){ //根据城市来推送

            List<CityInfo> cityInfoList = null;
            if(CollectionUtils.isEmpty(cityInfoList = positionMarketVo.getCityList())){
                return GenericResponse.ng("未选择推送的城市");
            }

            List<String> cityAreaCodeList = new ArrayList<>();
            for (CityInfo cityInfo : cityInfoList){
                cityAreaCodeList.add(cityInfo.getAreaCode());
            }

            List<UserPhone4PositionVo> userPhone4PositionList = userApi.getUserPhone4PositionByAreaCodeList(cityAreaCodeList);
            if(CollectionUtils.isNotEmpty(userPhone4PositionList)){
                //设置推送数量
                sendNum = userPhone4PositionList.size();
            }
        }else if(pushType == ConstantUtil.PUSH_BY_DISTANCE){ //根据距离来推送

            SitesInfo4Activity sitesInfo4Activity = sitesApi.getSitesInfo4Activity(shopId);
            if(sitesInfo4Activity == null){
                return GenericResponse.ng("景区不存在");
            }

            if(sitesInfo4Activity.getCenterLatitude() == null || sitesInfo4Activity.getCenterLongitude() == null){
                return GenericResponse.ng("景区中心经纬度不存在");
            }

            Long beginTime = null, endTime = null;
            DateTime now = new DateTime();
            DateTime todayBegin = new DateTime(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(),0,0);
            DateTime todayEnd = todayBegin.plusDays(1);

            int rangeDay = positionMarketVo.getRangeDay()==null?0:positionMarketVo.getRangeDay();
            if(rangeDay == 0){ //当天
                beginTime = todayBegin.getMillis();
                endTime = todayEnd.getMillis();
            }else if (rangeDay == 1){ //近1天
                beginTime = todayBegin.minusDays(1).getMillis();
                endTime = todayEnd.getMillis();
            }else if(rangeDay == 2){  //近2天
                beginTime = todayBegin.minusDays(2).getMillis();
                endTime = todayEnd.getMillis();
            }
            long maxDistance = positionMarketVo.getRangeM();
            List<UserTrackEntity> userTrackList = userTrackService.getUserTrackList(sitesInfo4Activity.getCenterLongitude(),
                    sitesInfo4Activity.getCenterLatitude(),maxDistance, beginTime, endTime);

            if(CollectionUtils.isNotEmpty(userTrackList)){
                List<UserTrackEntity> newTrackList = userTrackList.stream().filter(distinctByKey(item -> item.getRegId())).collect(Collectors.toList());
                //设置推送数量
                sendNum = newTrackList.size();
            }
        }else {
            return SendNumDataResponse.ng("只能选择城市或者按距离景区距离进行推送");
        }

        SendNumVo sendNumVo = new SendNumVo();
        sendNumVo.setSendNum(sendNum);
        return SendNumDataResponse.okWithData(sendNumVo);
    }



    @ApiOperation(value = "新增位置营销", notes = "新增位置营销")
    @ApiResponses({ @ApiResponse(code = 200, response = GenericResponse.class, message = "新增位置营销") })
    @PostMapping(value = "", produces = { MediaType.APPLICATION_JSON_UTF8_VALUE })
    public ResponseEntity<?> insert(@RequestBody PositionMarket4InsertVo positionMarketVo){

        String shopId = getShopId();
        PositionMarketEntity positionMarketEntity = new PositionMarketEntity();
        BeanUtils.copyProperties(positionMarketVo, positionMarketEntity);

        String positionMarketId = idGenerator.nextId();
        positionMarketEntity.setShopId(shopId);
        positionMarketEntity.setId(positionMarketId);

        DateTime triggerDate = null;
        if(StringUtils.isNotEmpty(positionMarketVo.getTriggerTimeStr())){
            DateTimeFormatter format = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");
            try {
                triggerDate = DateTime.parse(positionMarketVo.getTriggerTimeStr(), format);
                positionMarketEntity.setTriggerTime(triggerDate.getMillis());
                
                //进行触发时间的判断,只能大于当前时间
                long now = System.currentTimeMillis();
                if(now > triggerDate.getMillis()){
                    return GenericResponse.ng("只能选择大于当前的时间触发");
                }

            }catch (Exception e){
                return GenericResponse.ng("触发时间格式不对");
            }

        }else {
            //立即发送
            positionMarketEntity.setTriggerTime(System.currentTimeMillis());
        }

        int pushType = positionMarketEntity.getType()==null?0:positionMarketEntity.getType();
        //2.给谁发推送,
        if(pushType == ConstantUtil.PUSH_BY_CITY){ //根据城市来推送

            List<CityInfo> cityInfoList = null;
            if(CollectionUtils.isEmpty(cityInfoList = positionMarketEntity.getCityList())){
                return GenericResponse.ng("未选择推送的城市");
            }

            List<String> cityAreaCodeList = new ArrayList<>();
            for (CityInfo cityInfo : cityInfoList){
                cityAreaCodeList.add(cityInfo.getAreaCode());
            }

            List<UserPhone4PositionVo> userPhone4PositionList = userApi.getUserPhone4PositionByAreaCodeList(cityAreaCodeList);
            if(CollectionUtils.isNotEmpty(userPhone4PositionList)){
                //设置推送数量
                positionMarketEntity.setSendNum(userPhone4PositionList.size());

                PositionPushRecordEntity pushRecordEntity;
                for(UserPhone4PositionVo userPhone4PositionVo : userPhone4PositionList){
                    pushRecordEntity = new PositionPushRecordEntity();
                    BeanUtils.copyProperties(userPhone4PositionVo, pushRecordEntity);

                    pushRecordEntity.setId(idGenerator.nextId());
                    pushRecordEntity.setShopId(shopId);
                    pushRecordEntity.setPositionMarketId(positionMarketId);

                    positionPushRecordService.save(pushRecordEntity);
                }
            }

        }else if(pushType == ConstantUtil.PUSH_BY_DISTANCE){ //根据距离来推送

            SitesInfo4Activity sitesInfo4Activity = sitesApi.getSitesInfo4Activity(shopId);
            if(sitesInfo4Activity == null){
                return GenericResponse.ng("景区不存在");
            }

            if(sitesInfo4Activity.getCenterLatitude() == null || sitesInfo4Activity.getCenterLongitude() == null){
                return GenericResponse.ng("景区中心经纬度不存在");
            }

            Long beginTime = null, endTime = null;
            DateTime now = new DateTime();
            DateTime todayBegin = new DateTime(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(),0,0);
            DateTime todayEnd = todayBegin.plusDays(1);

            int rangeDay = positionMarketVo.getRangeDay()==null?0:positionMarketVo.getRangeDay();
            if(rangeDay == 0){ //当天
                beginTime = todayBegin.getMillis();
                endTime = todayEnd.getMillis();
            }else if (rangeDay == 1){ //近1天
                beginTime = todayBegin.minusDays(1).getMillis();
                endTime = todayEnd.getMillis();
            }else if(rangeDay == 2){  //近2天
                beginTime = todayBegin.minusDays(2).getMillis();
                endTime = todayEnd.getMillis();
            }
            long maxDistance = positionMarketVo.getRangeM();
            List<UserTrackEntity> userTrackList = userTrackService.getUserTrackList(sitesInfo4Activity.getCenterLongitude(),
                    sitesInfo4Activity.getCenterLatitude(),maxDistance, beginTime, endTime);
            //116.47345,40.026196
//            List<UserTrackEntity> userTrackList = userTrackService.getUserTrackList(116.47345,
//                    40.026196,maxDistance, beginTime, endTime);

            if(CollectionUtils.isNotEmpty(userTrackList)){
                List<UserTrackEntity> newTrackList = userTrackList.stream().filter(distinctByKey(item -> item.getRegId())).collect(Collectors.toList());
                //设置推送数量
                positionMarketEntity.setSendNum(newTrackList.size());

                for (UserTrackEntity userTrackEntity : newTrackList){
                    PositionPushRecordEntity positionPushRecordEntity = new PositionPushRecordEntity();
                    positionPushRecordEntity.setId(idGenerator.nextId());
                    positionPushRecordEntity.setPositionMarketId(positionMarketId);
                    positionPushRecordEntity.setRegId(userTrackEntity.getRegId());
                    positionPushRecordEntity.setUserId(userTrackEntity.getUserId());
                    positionPushRecordEntity.setOsType(userTrackEntity.getOsType());
                    positionPushRecordService.save(positionPushRecordEntity);
                }
            }
        }else {
            return GenericResponse.ng("只能选择城市或者按距离景区距离进行推送");
        }

        positionMarketService.save(positionMarketEntity);
        String jobKeyName = ConstantUtil.JOB_KEY_PREFIX_4_POSITION_MARKET + positionMarketId;
        String triggerKeyName = ConstantUtil.TRAGGER_KEY_PREFIX_4_POSITION_MARKET + positionMarketId;
        //3.添加定时器
        JobDetailImpl jobDetail = new JobDetailImpl();
        jobDetail.setKey(
                new JobKey(jobKeyName, ConstantUtil.JOB_GROUP_NAME_4_POSITION_MARKET));
        jobDetail.setJobClass(PositionMarketJob.class);
        jobDetail.setDurability(true);
        jobDetail.getJobDataMap().put(PositionMarketJob.POSITION_MARK_ID, positionMarketId);

        if(pushType == ConstantUtil.PUSH_BY_DISTANCE){ //根据距离来推送
            jobDetail.getJobDataMap().put(PositionMarketJob.PUSH_BY_TYPE, PositionMarketJob.PUSH_BY_LOCATION);
        }else if (pushType == ConstantUtil.PUSH_BY_CITY) { //根据城市来推送
            jobDetail.getJobDataMap().put(PositionMarketJob.PUSH_BY_TYPE, PositionMarketJob.PUSH_BY_CITY);
        }

        Trigger trigger;
        if(triggerDate != null) { //定时发送
            trigger = newTrigger().forJob(jobDetail)
                    .withIdentity(triggerKeyName,
                            ConstantUtil.TRAGGER_GROUP_NAME_4_POSITION_MARKET)
                    .withPriority(50)
                    .startAt(triggerDate.toDate())
                    .build();
        }else {  //立即发送
            trigger = newTrigger().forJob(jobDetail)
                    .withIdentity(triggerKeyName,
                            ConstantUtil.TRAGGER_GROUP_NAME_4_POSITION_MARKET)
                    .withPriority(50)
                    .startNow()
                    .build();
        }

        try {
            schedulerFactoryBean.getScheduler().addJob(jobDetail, true, true);
            if (!schedulerFactoryBean.getScheduler().checkExists(new TriggerKey(
                    triggerKeyName, ConstantUtil.TRAGGER_GROUP_NAME_4_POSITION_MARKET))) {
                schedulerFactoryBean.getScheduler().scheduleJob(trigger);
            }
        }catch (Exception e){
            logger.error("error message : {}", e);
        }

        return GenericResponse.ok();
    }

    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }


    public static class SendNumVo{

        @ApiModelProperty(name = "sendNum", value = "此次推送覆盖的人数", position=0)
        private long sendNum;

        public long getSendNum() {
            return sendNum;
        }
        public void setSendNum(long sendNum) {
            this.sendNum = sendNum;
        }
    }



}
